Completed
Pull Request — master (#328)
by
unknown
02:20
created

View._onClickDownloadSelected   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
/**
2
 * Nextcloud - Gallery
3
 *
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Olivier Paroz <[email protected]>
9
 *
10
 * @copyright Olivier Paroz 2017
11
 */
12
/* global Handlebars, Gallery, GalleryImage, Thumbnails */
13
(function ($, _, OC, t, Gallery) {
14
	"use strict";
15
16
	var TEMPLATE_ADDBUTTON = '<a href="#" class="button new"><span class="icon icon-add"></span><span class="hidden-visually">New</span></a>';
0 ignored issues
show
Coding Style introduced by
Line is too long.
Loading history...
17
	var TEMPLATE_DOWNLOADBUTTON = '<span id="selectedActionsList" class="selectedActions hidden">' +
18
		'<div href="#" class="button download"><span class="icon icon-download"></span>' +
19
		'<span class="hidden-visually">Download</span></div></span>';
20
21
	/**
22
	 * Builds and updates the Gallery view
23
	 *
24
	 * @constructor
25
	 */
26
	var View = function () {
27
		this.element = $('#gallery');
28
		this.loadVisibleRows.loading = false;
29
		this._setupUploader();
30
		this.breadcrumb = new Gallery.Breadcrumb();
31
		this.emptyContentElement = $('#emptycontent');
32
		this.controlsElement = $('#controls');
33
	};
34
35
	View.prototype = {
36
		element: null,
37
		breadcrumb: null,
38
		requestId: -1,
39
		emptyContentElement: null,
40
		controlsElement: null,
41
		/**
42
		 * Map of file id to file data
43
		 * @type Object.<int, Object>
44
		 */
45
		_selectedFiles: {},
46
		/**
47
		 * Summary of selected files.
48
		 * @type OCA.Files.FileSummary
49
		 */
50
		_selectionSummary: null,
51
		/**
52
		 * Initialiation status
53
		 * @type Boolean
54
		 */
55
		_initialized: false,
56
57
		/**
58
		 * Removes all thumbnails from the view
59
		 */
60
		clear: function () {
61
			this.loadVisibleRows.processing = false;
62
			this.loadVisibleRows.loading = null;
63
			// We want to keep all the events
64
			this.element.children().detach();
65
			this.showLoading();
66
		},
67
68
		/**
69
		 * @param {string} path
70
		 * @returns {boolean}
71
		 */
72
		_isValidPath: function(path) {
73
			var sections = path.split('/');
74
			for (var i = 0; i < sections.length; i++) {
75
				if (sections[i] === '..') {
76
					return false;
77
				}
78
			}
79
80
			return path.toLowerCase().indexOf(decodeURI('%0a')) === -1 &&
81
				path.toLowerCase().indexOf(decodeURI('%00')) === -1;
82
		},
83
84
		/**
85
		 * Populates the view if there are images or albums to show
86
		 *
87
		 * @param {string} albumPath
88
		 * @param {string|undefined} errorMessage
89
		 */
90
		init: function (albumPath, errorMessage) {
91
			// Set path to an empty value if not a valid one
92
			if(!this._isValidPath(albumPath)) {
93
				albumPath = '';
94
			}
95
96
			// Only do it when the app is initialised
97
			if (this.requestId === -1) {
98
				this._initButtons();
99
				this._blankUrl();
100
			}
101
			if ($.isEmptyObject(Gallery.imageMap)) {
102
				Gallery.view.showEmptyFolder(albumPath, errorMessage);
103
			} else {
104
				this.viewAlbum(albumPath);
105
			}
106
107
			this._setBackgroundColour();
108
109
			this._initSelection();
110
			this.initialized = true;
111
		},
112
113
		/**
114
		 * Starts the slideshow
115
		 *
116
		 * @param {string} path
117
		 * @param {string} albumPath
118
		 */
119
		startSlideshow: function (path, albumPath) {
120
			var album = Gallery.albumMap[albumPath];
121
			var images = album.images;
122
			var startImage = Gallery.imageMap[path];
123
			Gallery.slideShow(images, startImage, false);
124
		},
125
126
		/**
127
		 * Sets up the controls and starts loading the gallery rows
128
		 *
129
		 * @param {string|null} albumPath
130
		 */
131
		viewAlbum: function (albumPath) {
132
			albumPath = albumPath || '';
133
			if (!Gallery.albumMap[albumPath]) {
134
				return;
135
			}
136
137
			this.clear();
138
139
			if (albumPath !== Gallery.currentAlbum
140
				|| (albumPath === Gallery.currentAlbum &&
141
				Gallery.albumMap[albumPath].etag !== Gallery.currentEtag)) {
142
				Gallery.currentAlbum = albumPath;
143
				Gallery.currentEtag = Gallery.albumMap[albumPath].etag;
144
				this._setupButtons(albumPath);
145
			}
146
147
			Gallery.albumMap[albumPath].viewedItems = 0;
148
			Gallery.albumMap[albumPath].preloadOffset = 0;
149
150
			// Each request has a unique ID, so that we can track which request a row belongs to
151
			this.requestId = Math.random();
152
			Gallery.albumMap[Gallery.currentAlbum].requestId = this.requestId;
153
154
			// Loading rows without blocking the execution of the rest of the script
155
			setTimeout(function () {
156
				this.loadVisibleRows.activeIndex = 0;
157
				this.loadVisibleRows(Gallery.albumMap[Gallery.currentAlbum]);
158
			}.bind(this), 0);
159
		},
160
161
		/**
162
		 * Manages the sorting interface
163
		 *
164
		 * @param {string} sortType name or date
165
		 * @param {string} sortOrder asc or des
166
		 */
167
		sortControlsSetup: function (sortType, sortOrder) {
168
			var reverseSortType = 'date';
169
			if (sortType === 'date') {
170
				reverseSortType = 'name';
171
			}
172
			this._setSortButton(sortType, sortOrder, true);
173
			this._setSortButton(reverseSortType, 'asc', false); // default icon
174
		},
175
176
		/**
177
		 * Loads and displays gallery rows on screen
178
		 *
179
		 * view.loadVisibleRows.loading holds the Promise of a row
180
		 *
181
		 * @param {Album} album
182
		 */
183
		loadVisibleRows: function (album) {
184
			var view = this;
185
			// Wait for the previous request to be completed
186
			if (this.loadVisibleRows.processing) {
187
				return;
188
			}
189
190
			/**
191
			 * At this stage, there is no loading taking place, so we can look for new rows
192
			 */
193
194
			var scroll = $('#content-wrapper').scrollTop() + $(window).scrollTop();
195
			// 2 windows worth of rows is the limit from which we need to start loading new rows.
196
			// As we scroll down, it grows
197
			var targetHeight = ($(window).height() * 2) + scroll;
198
			// We throttle rows in order to try and not generate too many CSS resizing events at
199
			// the same time
200
			var showRows = _.throttle(function (album) {
201
202
				// If we've reached the end of the album, we kill the loader
203
				if (!(album.viewedItems < album.subAlbums.length + album.images.length)) {
0 ignored issues
show
Coding Style introduced by
The usage of ! looks confusing here.

The following shows a case which JSHint considers confusing and its respective non-confusing counterpart:

! (str.indexOf(i) > -1) // Bad
str.indexOf(i) === -1 // Good
Loading history...
204
					view.loadVisibleRows.processing = false;
205
					view.loadVisibleRows.loading = null;
206
					return;
207
				}
208
209
				// Prevents creating rows which are no longer required. I.e when changing album
210
				if (view.requestId !== album.requestId) {
211
					return;
212
				}
213
214
				// We can now safely create a new row
215
				var row = album.getRow($(window).width());
216
				var rowDom = row.getDom();
217
				view.element.append(rowDom);
218
219
				return album.fillNextRow(row).then(function () {
220
					if (album.viewedItems < album.subAlbums.length + album.images.length &&
221
						view.element.height() < targetHeight) {
222
						return showRows(album);
223
					}
224
					// No more rows to load at the moment
225
					view.loadVisibleRows.processing = false;
226
					view.loadVisibleRows.loading = null;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
227
				}, function () {
228
					// Something went wrong, so kill the loader
229
					view.loadVisibleRows.processing = false;
230
					view.loadVisibleRows.loading = null;
231
				});
232
			}, 100);
233
			if (this.element.height() < targetHeight) {
234
				this._showNormal();
235
				this.loadVisibleRows.processing = true;
236
				album.requestId = view.requestId;
237
				this.loadVisibleRows.loading = showRows(album);
238
			}
239
		},
240
241
		/**
242
		 * Shows an empty gallery message
243
		 *
244
		 * @param {string} albumPath
245
		 * @param {string|null} errorMessage
246
		 */
247
		showEmptyFolder: function (albumPath, errorMessage) {
248
			var message = '<div class="icon-gallery"></div>';
249
			var uploadAllowed = true;
250
251
			this.element.children().detach();
252
			this.removeLoading();
253
254
			if (!_.isUndefined(errorMessage) && errorMessage !== null) {
255
				message += '<h2>' + t('gallery',
256
						'Album cannot be shown') + '</h2>';
257
				message += '<p>' + escapeHTML(errorMessage) + '</p>';
0 ignored issues
show
Bug introduced by
escapeHTML does not seem to be defined.
Loading history...
258
				uploadAllowed = false;
259
			} else {
260
				message += '<h2>' + t('gallery',
261
						'No media files found') + '</h2>';
262
				// We can't upload yet on the public side
263
				if (Gallery.token) {
264
					message += '<p>' + t('gallery',
265
							'Upload pictures in the Files app to display them here') + '</p>';
266
				} else {
267
					message += '<p>' + t('gallery',
268
							'Upload new files via drag and drop or by using the [+] button above') +
269
						'</p>';
270
				}
271
			}
272
			this.emptyContentElement.html(message);
273
			this.emptyContentElement.removeClass('hidden');
274
275
			this._hideButtons(uploadAllowed);
276
			Gallery.currentAlbum = albumPath;
277
			var availableWidth = $(window).width() - Gallery.buttonsWidth;
278
			this.breadcrumb.init(albumPath, availableWidth);
279
			Gallery.config.albumDesign = null;
280
		},
281
282
		/**
283
		 * Dims the controls bar when retrieving new content. Matches the effect in Files
284
		 */
285
		dimControls: function () {
286
			// Use the existing mask if its already there
287
			var $mask = this.controlsElement.find('.mask');
288
			if ($mask.exists()) {
289
				return;
290
			}
291
			$mask = $('<div class="mask transparent"></div>');
292
			this.controlsElement.append($mask);
293
			$mask.removeClass('transparent');
294
		},
295
296
		/**
297
		 * Shows the infamous loading spinner
298
		 */
299
		showLoading: function () {
300
			this.emptyContentElement.addClass('hidden');
301
			this.controlsElement.removeClass('hidden');
302
			$('#content').addClass('icon-loading');
303
			this.dimControls();
304
		},
305
306
		/**
307
		 * Removes the spinner in the main area and restore normal visibility of the controls bar
308
		 */
309
		removeLoading: function () {
310
			$('#content').removeClass('icon-loading');
311
			this.controlsElement.find('.mask').remove();
312
		},
313
314
		/**
315
		 * Shows thumbnails
316
		 */
317
		_showNormal: function () {
318
			this.emptyContentElement.addClass('hidden');
319
			this.controlsElement.removeClass('hidden');
320
			this.removeLoading();
321
		},
322
323
		/**
324
		 * Sets up our custom handlers for folder uploading operations
325
		 *
326
		 * @see OC.Upload.init/file_upload_param.done()
327
		 *
328
		 * @private
329
		 */
330
		_setupUploader: function () {
331
			var $uploadEl = $('#file_upload_start');
332
			if (!$uploadEl.exists()) {
333
				return;
334
			}
335
			this._uploader = new OC.Uploader($uploadEl, {
336
				fileList: FileList,
0 ignored issues
show
Bug introduced by
The variable FileList seems to be never declared. If this is a global, consider adding a /** global: FileList */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
337
				dropZone: $('#content')
338
			});
339
			this._uploader.on('add', function (e, data) {
340
				data.targetDir = '/' + Gallery.currentAlbum;
341
			});
342
			this._uploader.on('done', function (e, upload) {
343
				var data = upload.data;
344
345
				// is that the last upload ?
346
				if (data.files[0] === data.originalFiles[data.originalFiles.length - 1]) {
347
					var fileList = data.originalFiles;
348
					//Ask for a refresh of the photowall
349
					Gallery.getFiles(Gallery.currentAlbum).done(function () {
350
						var fileId, path;
351
						// Removes the cached thumbnails of files which have been re-uploaded
352
						_(fileList).each(function (fileName) {
353
							path = Gallery.currentAlbum + '/' + fileName;
354
							if (Gallery.imageMap[path]) {
355
								fileId = Gallery.imageMap[path].fileId;
356
								if (Thumbnails.map[fileId]) {
357
									delete Thumbnails.map[fileId];
358
								}
359
							}
360
						});
361
362
						Gallery.view.init(Gallery.currentAlbum);
363
					});
364
				}
365
			});
366
367
			// Since Nextcloud 9.0
368
			if (OC.Uploader) {
369
				OC.Uploader.prototype._isReceivedSharedFile = function (file) {
370
					var path = file.name;
371
					var sharedWith = false;
372
373
					if (Gallery.currentAlbum !== '' && Gallery.currentAlbum !== '/') {
374
						path = Gallery.currentAlbum + '/' + path;
375
					}
376
					if (Gallery.imageMap[path] && Gallery.imageMap[path].sharedWithUser) {
377
						sharedWith = true;
378
					}
379
380
					return sharedWith;
381
				};
382
			}
383
		},
384
385
		/**
386
		 * Setups selection feature
387
		 * Also resets file actions list to be hidden in case of user navigating
388
		 * back and forth between albums
389
		 *
390
		 * @private
391
		 */
392
		_initSelection: function() {
393
			this._selectedFiles = {};
394
			this._selectionSummary = new OCA.Files.FileSummary();
395
			if (!this.initialized) {
396
				this.element.on('change', '.selectCheckBox', _.bind(this._onClickFileCheckbox, this));
397
			}
398
399
			var selectedActionsList = $('#selectedActionsList');
400
			if (selectedActionsList && !selectedActionsList.hasClass('hidden')) {
401
				selectedActionsList.addClass('hidden');
402
			}
403
		},
404
405
		/**
406
		 * Adds all the click handlers to buttons the first time they appear in the interface
407
		 *
408
		 * @private
409
		 */
410
		_initButtons: function () {
411
			this.element.on("contextmenu", function(e) { e.preventDefault(); });
412
			$('#filelist-button').click(Gallery.switchToFilesView);
413
			$('#download').click(Gallery.download);
414
			$('#shared-button').click(Gallery.share);
415
			Gallery.infoBox = new Gallery.InfoBox();
416
			$('#album-info-button').click(Gallery.showInfo);
417
			$('#sort-name-button').click(Gallery.sorter);
418
			$('#sort-date-button').click(Gallery.sorter);
419
			$('#save #save-button').click(Gallery.showSaveForm);
420
			$('.save-form').submit(Gallery.saveForm);
421
			this._renderDownloadSelectedButton();
422
			this._renderNewButton();
423
			// Trigger cancelling of file upload
424
			$('#uploadprogresswrapper .stop').on('click', function () {
425
				OC.Upload.cancelUploads();
426
			});
427
			this.requestId = Math.random();
428
		},
429
430
		/**
431
		 * Sets up all the buttons of the interface and the breadcrumbs
432
		 *
433
		 * @param {string} albumPath
434
		 * @private
435
		 */
436
		_setupButtons: function (albumPath) {
437
			this._shareButtonSetup(albumPath);
438
			this._infoButtonSetup();
439
440
			var availableWidth = $(window).width() - Gallery.buttonsWidth;
441
			this.breadcrumb.init(albumPath, availableWidth);
442
			var album = Gallery.albumMap[albumPath];
443
444
			var sum = album.images.length + album.subAlbums.length;
445
			//If sum of the number of images and subalbums exceeds 1 then show the buttons.
446
			if(sum > 1)
447
			{
448
				$('#sort-name-button').show();
449
				$('#sort-date-button').show();
450
			}
451
			else
452
			{
453
				$('#sort-name-button').hide();
454
				$('#sort-date-button').hide();
455
			}
456
			var currentSort = Gallery.config.albumSorting;
457
			this.sortControlsSetup(currentSort.type, currentSort.order);
458
			Gallery.albumMap[Gallery.currentAlbum].images.sort(
459
				Gallery.utility.sortBy(currentSort.type,
460
					currentSort.order));
461
			Gallery.albumMap[Gallery.currentAlbum].subAlbums.sort(Gallery.utility.sortBy('name',
462
				currentSort.albumOrder));
463
464
			$('#save-button').show();
465
			$('#download').show();
466
			$('a.button.new').show();
467
		},
468
469
		/**
470
		 * Hide buttons in the controls bar
471
		 *
472
		 * @param uploadAllowed
473
		 */
474
		_hideButtons: function (uploadAllowed) {
475
			$('#album-info-button').hide();
476
			$('#shared-button').hide();
477
			$('#sort-name-button').hide();
478
			$('#sort-date-button').hide();
479
			$('#save-button').hide();
480
			$('#download').hide();
481
482
			if (!uploadAllowed) {
483
				$('a.button.new').hide();
484
			}
485
		},
486
487
		/**
488
		 * Shows or hides the share button depending on if we're in a public gallery or not
489
		 *
490
		 * @param {string} albumPath
491
		 * @private
492
		 */
493
		_shareButtonSetup: function (albumPath) {
494
			var shareButton = $('#shared-button');
495
			if (albumPath === '' || Gallery.token) {
496
				shareButton.hide();
497
			} else {
498
				shareButton.show();
499
			}
500
		},
501
502
		/**
503
		 * Shows or hides the info button based on the information we've received from the server
504
		 *
505
		 * @private
506
		 */
507
		_infoButtonSetup: function () {
508
			var infoButton = $('#album-info-button');
509
			infoButton.find('span').hide();
510
			var infoContentContainer = $('.album-info-container');
511
			infoContentContainer.slideUp();
512
			infoContentContainer.css('max-height',
513
				$(window).height() - Gallery.browserToolbarHeight);
514
			var albumInfo = Gallery.config.albumInfo;
515
			if (Gallery.config.albumError) {
516
				infoButton.hide();
517
				var text = '<strong>' + t('gallery', 'Configuration error') + '</strong></br>' +
518
					Gallery.config.albumError.message + '</br></br>';
519
				Gallery.utility.showHtmlNotification(text, 7);
520
			} else if ($.isEmptyObject(albumInfo)) {
521
				infoButton.hide();
522
			} else {
523
				infoButton.show();
524
				if (albumInfo.inherit !== 'yes' || albumInfo.level === 0) {
525
					infoButton.find('span').delay(1000).slideDown();
526
				}
527
			}
528
		},
529
530
		/**
531
		 * Sets the background colour of the photowall
532
		 *
533
		 * @private
534
		 */
535
		_setBackgroundColour: function () {
536
			var wrapper = $('#content-wrapper');
537
			var albumDesign = Gallery.config.albumDesign;
538
			if (!$.isEmptyObject(albumDesign) && albumDesign.background) {
539
				wrapper.css('background-color', albumDesign.background);
540
			} else {
541
				wrapper.css('background-color', '#fff');
542
			}
543
		},
544
545
		/**
546
		 * Picks the image which matches the sort order
547
		 *
548
		 * @param {string} sortType name or date
549
		 * @param {string} sortOrder asc or des
550
		 * @param {boolean} active determines if we're setting up the active sort button
551
		 * @private
552
		 */
553
		_setSortButton: function (sortType, sortOrder, active) {
554
			var button = $('#sort-' + sortType + '-button');
555
			// Removing all the classes which control the image in the button
556
			button.removeClass('active');
557
			button.find('img').removeClass('front');
558
			button.find('img').removeClass('back');
559
560
			// We need to determine the reverse order in order to send that image to the back
561
			var reverseSortOrder = 'des';
562
			if (sortOrder === 'des') {
563
				reverseSortOrder = 'asc';
564
			}
565
566
			// We assign the proper order to the button images
567
			button.find('img.' + sortOrder).addClass('front');
568
			button.find('img.' + reverseSortOrder).addClass('back');
569
570
			// The active button needs a hover action for the flip effect
571
			if (active) {
572
				button.addClass('active');
573
				if (button.is(":hover")) {
574
					button.removeClass('hover');
575
				}
576
				// We can't use a toggle here
577
				button.hover(function () {
578
						$(this).addClass('hover');
579
					},
580
					function () {
581
						$(this).removeClass('hover');
582
					});
583
			}
584
		},
585
586
		/**
587
		 * If no url is entered then do not show the error box.
588
		 *
589
		 */
590
		_blankUrl: function() {
591
			$('#remote_address').on("change keyup paste", function() {
592
 				if ($(this).val() === '') {
593
 					$('#save-button-confirm').prop('disabled', true);
594
 				} else {
595
 					$('#save-button-confirm').prop('disabled', false);
596
 				}
597
			});
598
		},
599
600
		/**
601
		 * Creates the [+] button allowing users who can't drag and drop to upload files
602
		 *
603
		 * @see core/apps/files/js/filelist.js
604
		 * @private
605
		 */
606
		_renderNewButton: function () {
607
			// if no actions container exist, skip
608
			var $actionsContainer = $('.actions.creatable');
609
			if (!$actionsContainer.length) {
610
				return;
611
			}
612
			if (!this._addButtonTemplate) {
613
				this._addButtonTemplate = Handlebars.compile(TEMPLATE_ADDBUTTON);
614
			}
615
			var $newButton = $(this._addButtonTemplate({
616
				addText: t('gallery', 'New'),
617
				iconUrl: OC.imagePath('core', 'actions/add')
618
			}));
619
620
			$actionsContainer.prepend($newButton);
621
			$newButton.tooltip({'placement': 'bottom'});
622
623
			$newButton.click(_.bind(this._onClickNewButton, this));
624
			this._newButton = $newButton;
625
		},
626
627
		/**
628
		 * Creates the click handler for the [+] button
629
		 * @param event
630
		 * @returns {boolean}
631
		 *
632
		 * @see core/apps/files/js/filelist.js
633
		 * @private
634
		 */
635
		_onClickNewButton: function (event) {
636
			var $target = $(event.target);
637
			if (!$target.hasClass('.button')) {
638
				$target = $target.closest('.button');
639
			}
640
			this._newButton.tooltip('hide');
641
			event.preventDefault();
642
			if ($target.hasClass('disabled')) {
643
				return false;
644
			}
645
			if (!this._newFileMenu) {
646
				this._newFileMenu = new Gallery.NewFileMenu();
647
				$('.actions').append(this._newFileMenu.$el);
648
			}
649
			this._newFileMenu.showAt($target);
650
651
			if (Gallery.currentAlbum === '') {
652
				$('.menuitem[data-action="hideAlbum"]').parent().hide();
653
			}
654
			return false;
655
		},
656
657
		/**
658
		 * Creates the download individual files button
659
		 *
660
		 * @see core/apps/files/js/filelist.js
661
		 * @private
662
		 */
663
		_renderDownloadSelectedButton: function () {
664
			if (!this._downloadButtonTemplate) {
665
				this._downloadButtonTemplate = Handlebars.compile(TEMPLATE_DOWNLOADBUTTON);
666
			}
667
			var $downloadButton = $(this._downloadButtonTemplate({
668
				addText: t('gallery', 'Download'),
669
				iconUrl: OC.imagePath('core', 'actions/download')
670
			}));
671
672
			$('#filelist-button').before($downloadButton);
673
			$downloadButton.tooltip({'placement': 'bottom'});
674
675
			$downloadButton.click(_.bind(this._onClickDownloadSelected, this));
676
		},
677
678
		/**
679
		 * Event handler for when clicking on "Download" for the selected files
680
		 *
681
		 * @see core/apps/files/js/filelist.js
682
		 * @private
683
		 */
684
		_onClickDownloadSelected: function(event) {
685
			var files;
686
			var dir = Gallery.currentAlbum;
687
			if (dir === '') {
688
				dir = '/';
689
			}
690
			files = _.pluck(this.getSelectedFiles(), 'path');
691
			files.forEach(function(file, index, files) {
692
				files[index] = OC.basename(file);
693
			});
694
695
			var downloadFileActionIcon = $('#selectedActionsList').find('.download .icon');
696
697
			// don't allow a second click on the download action
698
			if (downloadFileActionIcon.hasClass('icon-loading-small')) {
699
				event.preventDefault();
700
				return;
701
			}
702
703
			var disableLoadingState = function() {
704
				downloadFileActionIcon.removeClass('icon-loading-small');
705
				downloadFileActionIcon.addClass('icon-download');
706
			};
707
			downloadFileActionIcon.removeClass('icon-download');
708
			downloadFileActionIcon.addClass('icon-loading-small');
709
710
			if(this.getSelectedFiles().length > 1) {
711
				OCA.Files.Files.handleDownload(Gallery.getSelectionDownloadUrl(files, dir), disableLoadingState);
712
			}
713
			else {
714
				var first = OC.basename(this.getSelectedFiles()[0].path);
715
				OCA.Files.Files.handleDownload(Gallery.getSelectionDownloadUrl(first, dir), disableLoadingState);
716
			}
717
			return false;
718
	},
719
720
		/**
721
		 * Returns the file info of the selected files
722
		 *
723
		 * @return array of file names
724
		 *
725
		 * @see core/apps/files/js/filelist.js
726
		 * @private
727
		 */
728
		getSelectedFiles: function() {
729
			return _.values(this._selectedFiles);
730
		},
731
732
		/**
733
		 * Returns the file data from a given file element.
734
		 * @param $el file tr element
735
		 * @return file data
736
		 *
737
		 * @see core/apps/files/js/filelist.js
738
		 * @private
739
		 */
740
		elementToFile: function($el){
741
			$el = $($el);
742
			var data = {
743
				id: parseInt($el.attr('data-id'), 10),
744
			};
745
			var path = $el.attr('data-path');
746
			if (path) {
747
				data.path = path;
748
			}
749
			return data;
750
		},
751
752
		/**
753
		 * Selected/deselects the given file element and updated
754
		 * the internal selection cache.
755
		 *
756
		 * @param {Object} $element single image
757
		 * @param {bool} state true to select, false to deselect
758
		 *
759
		 * @see core/apps/files/js/filelist.js
760
		 * @private
761
		 */
762
		_selectFileEl: function($element, state) {
763
			var $checkbox = $element.find('row-element>.image-label>.selectCheckBox');
764
			var oldData = !!this._selectedFiles[$element.data('id')];
765
			var data;
766
			$checkbox.prop('checked', state);
767
			$element.toggleClass('selected', state);
768
			// already selected ?
769
			if (state === oldData) {
770
				return;
771
			}
772
			data = this.elementToFile($element);
773
			if (state) {
774
				this._selectedFiles[$element.data('id')] = data;
775
				this._selectionSummary.add(data);
776
			}
777
			else {
778
				delete this._selectedFiles[$element.data('id')];
779
				this._selectionSummary.remove(data);
780
			}
781
		},
782
783
		/**
784
		 * Event handler for when clicking on a element's checkbox
785
		 *
786
		 * @see core/apps/files/js/filelist.js
787
		 * @private
788
		 */
789
		_onClickFileCheckbox: function(event) {
790
			var $image = $(event.target).closest('.' + GalleryImage.cssClass);
791
			var state = !$image.hasClass('selected');
792
			this._selectFileEl($image, state);
793
			this._lastChecked = $image;
794
			this.updateSelectionSummary();
795
		},
796
797
		/**
798
		 * Update UI based on the current selection
799
		 *
800
		 * @see core/apps/files/js/filelist.js
801
		 * @private
802
		 */
803
		updateSelectionSummary: function() {
804
			var summary = this._selectionSummary.summary;
805
806
			if (summary.totalFiles === 0 && summary.totalDirs === 0) {
807
				$('#selectedActionsList').addClass('hidden');
808
			}
809
			else {
810
				$('#selectedActionsList').removeClass('hidden');
811
			}
812
		},
813
814
	};
815
816
	Gallery.View = View;
817
})(jQuery, _, OC, t, Gallery);
818